import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

import net.roarsoftware.lastfm.*;

public class PandoraLastFm {

  public static void main(String[] args) {
    new PandoraLastFm().realMain(args);
  }

  private Thread  updater;
  private boolean going = false;

  private final Pandora               pandora = Pandora.getInstance();
  private final ApplicationController app = pandora.getApp();
  private final Properties            props;
  private       LastFm                lastfm;

  private JLabel trackLabel;
  private JLabel artistLabel;
  private JLabel albumLabel;
  private JLabel msgLabel;

  private final static String PROP_USERNAME = "username";
  private final static String PROP_USE_LAST_FM = "useLastFm";

  public PandoraLastFm() {
    System.setProperty("com.apple.mrj.application.apple.menu.about.name", "PandoraLastFm");
    System.setProperty("apple.laf.useScreenMenuBar", "true");
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException _) {
    } catch (InstantiationException _) {
    } catch (IllegalAccessException _) {
    } catch (UnsupportedLookAndFeelException _) {}
    props = loadProperties();
  }

  private final class ButtonAction extends JButton {
    ButtonAction(String icon, final String action) {
      this(icon, new Runnable() {public void run() {app.execute(action); showMessage(action);}});
    }
    ButtonAction(String icon, final Runnable r) {
      super(icon(icon));
      addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            r.run();
          }
        });
    }
  }

  static ImageIcon icon(String icon) {
    return new ImageIcon(icon);
  }

  public void realMain(String[] args) {

    // First try to open PandoraBoy
    try {
      Runtime.getRuntime().exec("open", new String[]{"/Applications/PandoraBoy.app/"});
    } catch (IOException e) {
      handle(e,"Couldn't start PandoraBoy");
    }

    // Set up the frame
    JFrame f = new JFrame(getClass().getName());
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().setLayout(new BoxLayout(f.getContentPane(), BoxLayout.Y_AXIS));

    // Menu bar
    f.setJMenuBar(createMenuBar());
    
    // Top Panel
    JPanel top = xPanel(f);

    // Set up all the actions
    ButtonAction[] actions = {
      new ButtonAction("Down24.gif","thumbs down"),
      new ButtonAction("Up24.gif",new Runnable() {
          public void run() {
            pandora.thumbsUp();
            showMessage("thumbs up");
            if (getBooleanProperty(PROP_USE_LAST_FM)) {
              String artist = getArtist();
              String track = getTrack();
              String album = getAlbum();
              Result r = addTrackToLastFm(artist,track,album);
              if (r == null) {
                showError("Please open the preferences");
              } else if (!r.isSuccessful()) {
                showError(r.getErrorMessage());
              } else {
                showMessage("Added " + track + " by " + artist);
              }
            }
          }
        }),
      new ButtonAction("Pause24.gif","pauseplay"),
      new ButtonAction("Play24.gif","pauseplay"),
      new ButtonAction("FastForward24.gif", new Runnable() {
          public void run() {
            pandora.nextTrack();
            showMessage("next track");
            updateLabels();
          }
        }),
    };
    for (ButtonAction action : actions) top.add(action);
    
    // Bottom panel
    JPanel bottom = yPanel(f);
    trackLabel = leftPanel(bottom);
    artistLabel = leftPanel(bottom);
    albumLabel = leftPanel(bottom);
    msgLabel = leftPanel(bottom);
    JPanel prefPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    prefPanel.add(new JButton(new AbstractAction("Preferences") {
        public void actionPerformed(ActionEvent e) {
          showPreferences();
        }
      }));
    prefPanel.add(new JButton(new AbstractAction("Quit") {
        public void actionPerformed(ActionEvent e) {
          System.exit(0);
        }
      }));
    bottom.add(prefPanel);

    going = true;
    updater = new Thread(new Runnable() {
        public void run() {
          while (going) {
            updateLabels();
            try {
              Thread.sleep(5 * 1000);
            } catch (InterruptedException _) {}
          }
        }
      });
    updater.start();

    // Show the frame
    f.pack();
    f.setVisible(true);

    // For quitting
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
          saveProperties();
          going = false;
          if (updater != null) {
            try {
              updater.join();
            } catch (InterruptedException _) {}
          }
        }
      }));
  }

  private JMenuBar createMenuBar() {
    JMenuBar res = new JMenuBar();
    JMenu file = new JMenu("File");
    JMenuItem prefs = new JMenuItem(new AbstractAction("Preferences") {
        public void actionPerformed(ActionEvent e) {
          showPreferences();
        }});
    JMenuItem quit = new JMenuItem(new AbstractAction("Quit") {
        public void actionPerformed(ActionEvent e) {
          System.exit(0);
        }});
    return res;
  }

  private void showMessage(String msg) {
    showMessage(Color.green.darker(),msg);
  }

  private void showError(String msg) {
    showMessage(Color.red.darker(),msg);
  }

  private void showMessage(Color c, String msg) {
    msgLabel.setForeground(c);
    msgLabel.setText(msg);
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          try {Thread.sleep(5000);} catch (Exception _) {}
          showMessage(Color.black,"Ready");
        }
      });
  }

  private Properties loadProperties() {
    Properties props = new Properties();
    File propsFile = getPropsFile();
    if (propsFile.exists()) {
      try {
        props.load(new FileInputStream(propsFile));
      } catch (IOException e) {
        handle(e);
      }
    }
    return props;
  }
  
  private void saveProperties() {
    try {
      props.save(new FileOutputStream(getPropsFile()), getClass().getSimpleName());
    } catch (FileNotFoundException e) {
      handle(e);
    }
  }
  
  private File getPropsFile() {
    return new File(System.getProperty("user.home"), "." + getClass().getSimpleName());
  }

  private void handle(Throwable e) {
    handle(e,null);
  }

  private void handle(Throwable e, String msg) {
    e.printStackTrace();
    if (msg != null) System.err.println(msg);
  }

  private void updateLabels() {
    trackLabel.setText(pandora.getCurrentTrack());
    artistLabel.setText(pandora.getCurrentArtist());
    albumLabel.setText(pandora.getCurrentAlbum());
  }

  private LastFm lastfm() {
    if (lastfm == null) {
      String username = getUsername();
      if (username != null) {
        lastfm = new LastFm(username);
      }
    }
    return lastfm;
  }

  private Result addTrackToLastFm(String artist,String track,String album) {
    Playlist playlist = null;
    LastFm lastfm = lastfm();
    if (lastfm == null) return null;
    for (Playlist pl : lastfm().getPlaylists()) {
      playlist = pl;
      break;
    }
    Result res = null;
    if (playlist != null) {
      res = lastfm.addTrack(playlist,artist,track);
    }
    return res;
  }

  private String getTrack() {
    return getText(trackLabel);
  }

  private String getArtist() {
    return getText(artistLabel);
  }

  private String getAlbum() {
    return getText(albumLabel);
  }

  private String getText(JLabel label) {
    return label == null ? null : label.getText();
  }

  private JLabel leftPanel(JPanel into) {
    JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
    JLabel res = new JLabel("...");
    p.add(res);
    into.add(p);
    return res;
  }

  private JPanel xPanel(JFrame f) {
    return panel(f,BoxLayout.X_AXIS);
  }

  private JPanel yPanel(JFrame f) {
    return panel(f,BoxLayout.Y_AXIS);
  }

  private JPanel panel(JFrame f, int align) {
    JPanel p = new JPanel();
    p.setLayout(new BoxLayout(p,align));
    if (f != null) f.getContentPane().add(p);
    return p;
  }

  // --------------------------------------------------
  // Properties
  // --------------------------------------------------

  private String getProperty(String key) {
    return props.getProperty(key);
  }

  private String getProperty(String key, String defaultValue) {
    return props.getProperty(key,defaultValue);
  }

  private int getIntProperty(String key) {
    String val = getProperty(key,"");
    return val.equals("") ? 0 : Integer.parseInt(val);
  }

  private boolean getBooleanProperty(String key) {
    String val = getProperty(key,"");
    return !(val.equals("") || val.toLowerCase().equals("false"));
  }

  private String setProperty(String key, String val) {
    Object o = props.setProperty(key,val);
    return o == null ? null : String.valueOf(o);
  }

  private int setIntProperty(String key, String val) {
    int v = Integer.parseInt(val);
    setIntProperty(key,v);
    return v;
  } 
  
  private int setIntProperty(String key, int val) {
    setProperty(key,String.valueOf(val));
    return val;
  } 

  private boolean setBooleanProperty(String key, boolean val) {
    setProperty(key,String.valueOf(val));
    return val;
  } 

  // --------------------------------------------------
  // Preferences
  // --------------------------------------------------

  private JFrame prefsFrame;
  private PrefsDialog prefsDialog;
  private class PrefsDialog extends JPanel {

    private final JTextField username = new JTextField(getProperty(PROP_USERNAME),30);
    private final JCheckBox useLastFm = new JCheckBox("Use last.fm");

    PrefsDialog(final JFrame f) {

      setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));

      f.getContentPane().setLayout(new BorderLayout());

      JPanel mid = new JPanel();
      mid.setLayout(new BoxLayout(mid,BoxLayout.Y_AXIS));
      add(mid,BorderLayout.CENTER);
      
      JPanel p = new JPanel();
      mid.add(p);
      p.setBorder(BorderFactory.createTitledBorder("Last.fm username"));
      p.add(username);

      useLastFm.setSelected(getBooleanProperty(PROP_USE_LAST_FM));
      mid.add(useLastFm);

     
      JPanel bottom = new JPanel(new FlowLayout(FlowLayout.RIGHT));
      add(bottom, BorderLayout.SOUTH);
      bottom.add(new JButton(new AbstractAction("OK") {
          public void actionPerformed(ActionEvent e) {
            save();
            f.dispose();
            f.setVisible(false);
          }
        }));
      bottom.add(new JButton(new AbstractAction("Cancel") {
          public void actionPerformed(ActionEvent e) {
            f.dispose();
            f.setVisible(false);
          }
        }));add(bottom,BorderLayout.SOUTH);
    }
    public void save() {
      setProperty(PROP_USERNAME, username.getText());
      setBooleanProperty(PROP_USE_LAST_FM, useLastFm.isSelected());
    }
  }

  private void showPreferences() {

    if (prefsFrame != null && prefsFrame.isVisible()) return;

    //Create and set up the window.
    JFrame frame = new JFrame("Preferences");
    prefsFrame = frame;
    
    //Create and set up the content pane.
    prefsDialog = new PrefsDialog(frame);
    frame.getContentPane().add(prefsDialog);
    
    //Display the window.
    frame.pack();
    frame.setVisible(true);
  }

  private String getUsername() {
    return getProperty(PROP_USERNAME);
  }

  private boolean useLastFm() {
    return getBooleanProperty(PROP_USE_LAST_FM);
  }
  
}